var LR = LR || {};

/**
 * 功能辅助区
 * THREE.Vector3Array
 * THREE.DataDictionary：数据字典，用来查找数据
 * THREE.RotatingObject : 绕给定的点旋转物体
 */
(function (THREE) {
  /**
   * 三维点的数组，提供获取点的最大最小值以及中间值。
   * @param vector3Array
   * @constructor
   */
  THREE.Vector3Array = function (vector3Array) {
    this.vector3Array = vector3Array || [];

    this.max = new THREE.Vector3();
    this.min = new THREE.Vector3();

    this._getMaxMin();
  };
  Object.assign(THREE.Vector3Array.prototype, {
    /**
     * 计算最大最小值
     * @private
     */
    _getMaxMin: function () {
      if (this.vector3Array.length > 0) {
        this.max = this.vector3Array[0].clone();
        this.min = this.vector3Array[0].clone();

        for (var i = 1; i < this.vector3Array.length; i++) {
          if (this.max.x < this.vector3Array[i].x) {
            this.max.x = this.vector3Array[i].x;
          }
          if (this.max.y < this.vector3Array[i].y) {
            this.max.y = this.vector3Array[i].y;
          }
          if (this.max.z < this.vector3Array[i].z) {
            this.max.z = this.vector3Array[i].z;
          }

          if (this.min.x > this.vector3Array[i].x) {
            this.min.x = this.vector3Array[i].x;
          }
          if (this.min.y > this.vector3Array[i].y) {
            this.min.y = this.vector3Array[i].y;
          }
          if (this.min.z > this.vector3Array[i].z) {
            this.min.z = this.vector3Array[i].z;
          }
        }
      }
    },

    /**
     * 获取中间位置坐标
     * @returns {*}
     */
    getCenter: function () {
      var center = new THREE.Vector3();
      return center.addVectors(this.max, this.min).multiplyScalar(0.5);
    }
  });

  /**
   * 提供一些对场景物体的查找、计算的公共方法。
   * @constructor
   */
  THREE.DataDictionary = function () {
  };
  Object.assign(THREE.DataDictionary.prototype, {
    /**
     * 根据给定objectID，查找对应的物体
     * @param object 查找范围
     * @param objectID
     */
    findObjectByID: function (object, objectID) {
      var selectObject = null;
      findObject(object, objectID);

      function findObject(object, objectID) {
        for (var i = 0; i < object.children.length; i++) {
          if (object.children[i].userData.instance && (object.children[i].userData.instance.id === objectID.toString())) {
            selectObject = object.children[i];
          } else {
            if (object.children[i].constructor === THREE.Object3D) {
              findObject(object.children[i], objectID)
            }
          }
        }
      }

      return selectObject;
    },

    /**
     * 根据给定objectID，查找对应的物体
     * @param object 查找范围
     * @param objectID 物体id
     * @param type 类型
     */
    findMeshByID: function (object, objectID, type) {
      var select_object = this.findObjectByID(object, objectID);
      var mesh = null;

      if (select_object) {
        findMesh(select_object, type);
      }

      function findMesh(object, type) {
        if (object.constructor === THREE.Mesh) {
          if (object.userData.type === type) {
            mesh = object;
          }
        } else if (object.constructor === THREE.Object3D || object.constructor === THREE.Group) {
          for (var i = 0; i < object.children.length; i++) {
            findMesh(object.children[i], type);
          }
        }
      }

      return mesh;
    },

    /**
     * 查找object中的所有mesh
     * @param object
     * @param meshes{Array}
     */
    getAllMesh: function (object, meshes) {
      if (object.constructor === THREE.Mesh) {
        meshes.push(object);
      } else {
        for (var i = 0; i < object.children.length; i++) {
          this.getAllMesh(object.children[i], meshes);
        }
      }
    },

    /**
     * 获取当前材质上的顶点
     * @param mesh
     * @param materialIndex {Array}
     * @returns {Array}
     */
    getMaterialVertexes: function (mesh, materialIndex) {
      var newFaces = [];
      var vertexIndexes = [];
      var newVertex = [];

      if (mesh) {
        var geometry = mesh.geometry;
        var faces = geometry.faces;
        var vertices = geometry.vertices;

        // 找到所有的面
        for (var i = 0; i < faces.length; i++) {
          if (faces[i].materialIndex === materialIndex) {
            newFaces.push(faces[i]);

            // 获取顶点的索引
            if (vertexIndexes.indexOf(faces[i].a) === -1) {
              vertexIndexes.push(faces[i].a);

              newVertex.push(vertices[faces[i].a]);
            }
            if (vertexIndexes.indexOf(faces[i].b) === -1) {
              vertexIndexes.push(faces[i].b);

              newVertex.push(vertices[faces[i].a]);
            }
            if (vertexIndexes.indexOf(faces[i].c) === -1) {
              vertexIndexes.push(faces[i].c);

              newVertex.push(vertices[faces[i].a]);
            }
          }
        }
      }

      mesh = null;
      newFaces = null;
      vertexIndexes = null;
      geometry = null;
      faces = null;
      vertices = null;

      return newVertex;
    },

    /**
     * 得到vec3的全局坐标
     * @param vec3
     * @param mesh
     * @private
     */
    getWorldPosition: function (vec3, mesh) {
      getWorldPosition(vec3, mesh);

      function getWorldPosition(vec3, object) {
        vec3.applyMatrix4(object.matrix);
        if (object.parent.constructor === THREE.Object3D || object.parent.constructor === THREE.Group) {
          getWorldPosition(vec3, object.parent);
        }
      }
    },

    /**
     * 获取给定物体的中心坐标
     * object
     */
    getObjectCenter: function (object) {
      var meshes = [];
      this.getAllMesh(object, meshes);
      return this.getMeshesCenter(meshes);
    },

    /**
     * 计算object的BoundingBox
     */
    computeBoundingBox: function (object) {
      var meshes = [];
      this.getAllMesh(object, meshes);
      return this.getMeshesBox(meshes);
    },

    /**
     * 获取所给定mesh数组中mesh的中心点坐标
     * @param meshes
     */
    getMeshesCenter: function (meshes) {
      var arr = [];
      for (var i = 0; i < meshes.length; i++) {
        if (!meshes[i].geometry.boundingBox) {
          meshes[i].geometry.computeBoundingBox();
        }
        var max = meshes[i].geometry.boundingBox.max.clone();
        this.getWorldPosition(max, meshes[i]);
        var min = meshes[i].geometry.boundingBox.min.clone();
        this.getWorldPosition(min, meshes[i]);
        arr.push(max);
        arr.push(min);
      }
      var vector3Array = new THREE.Vector3Array(arr);
      return vector3Array.getCenter();
    },
    /**
     * 获取盒子
     * @return {THREE.Vector3Array}
     */
    getMeshesBox: function (meshes) {
      var arr = [];
      for (var i = 0; i < meshes.length; i++) {
        if (!meshes[i].geometry.boundingBox) {
          meshes[i].geometry.computeBoundingBox();
        }
        var max = meshes[i].geometry.boundingBox.max.clone();
        this.getWorldPosition(max, meshes[i]);
        var min = meshes[i].geometry.boundingBox.min.clone();
        this.getWorldPosition(min, meshes[i]);
        arr.push(max);
        arr.push(min);
      }
      return new THREE.Vector3Array(arr);
    }
  });

  /**
   * 功能：绕给定的点旋转物体
   * 原理：在物体旋转之前算出给定点的世界坐标，物体旋转后，算出给顶点的世界坐标，
   *      用两个坐标的差值来移动旋转后的物体，使物体看起来像绕这给顶点进行旋转
   */
  THREE.RotatingObject = function (mesh) {
    this.mesh = mesh;
    this.origin = new THREE.Vector3();
    this.init();
  };
  Object.assign(THREE.RotatingObject.prototype, {
    init: function () {
      if (!this.mesh.geometry.boundingBox) {
        this.mesh.geometry.computeBoundingBox();
      }

      var min = this.getBoundingBoxMin().clone();
      var max = this.getBoundingBoxMax().clone();

      var vec = min.clone();
      vec.x = max.x;

      this.origin = vec.clone();
    },
    rotate: function (rat) {
      var pro = this.origin.clone();
      pro.applyMatrix4(this.mesh.matrix);

      this.mesh.rotateX(rat.x);
      this.mesh.rotateY(rat.y);
      this.mesh.rotateZ(rat.z);
      this.mesh.updateMatrix();

      var cp = this.origin.clone();
      cp.applyMatrix4(this.mesh.matrix);

      var vec = new THREE.Vector3();
      vec.subVectors(pro, cp);

      this._setPosition(vec);
    },
    rotateX: function (rat) {
      var vec = new THREE.Vector3(rat, 0, 0);
      this.rotate(vec);
    },
    rotateY: function (rat) {
      var vec = new THREE.Vector3(0, rat, 0);
      this.rotate(vec);
    },
    rotateZ: function (rat) {
      var vec = new THREE.Vector3(0, 0, rat);
      this.rotate(vec);
    },
    _setPosition: function (pos) {
      this.mesh.position.x += pos.x;
      this.mesh.position.y += pos.y;
      this.mesh.position.z += pos.z;
    },
    getBoundingBoxMin: function () {
      return this.mesh.geometry.boundingBox.min.clone();
    },
    getBoundingBoxMax: function () {
      return this.mesh.geometry.boundingBox.max.clone();
    },
    setOrigin: function (vec) {
      this.origin = vec.clone();
    },
    /**
     * 旋转中心点设置为网格的中心点
     */
    setOriginCenter: function () {
      this.origin = this.mesh.geometry.boundingSphere.center.clone();
    }
  });
})(THREE);

/**
 * 功能区
 * THREE.RotatingFans：旋转的风机集合
 * THREE.ElevatorLifts：电梯的集合
 * THREE.Tanks：水箱集合
 * LR.MusicControl:音乐控制
 * LR.RotatingFans: 旋转风机
 */
(function (THREE) {
  /**
   * 根据参数生成天空盒子加到场景的背景上
   */
  THREE.SkyBox = function (scene, baseFileUrl) {
    this.scene = scene;
    this.baseFileUrl = baseFileUrl !== undefined ? baseFileUrl : "source/textures/cube/";
    this.cubePic = {
      "skyboxsun25deg": ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
      "skybox": ['px.jpg', 'nx.jpg', 'py.jpg', 'ny.jpg', 'pz.jpg', 'nz.jpg'],
      "MilkyWay": ['dark-s_px.jpg', 'dark-s_nx.jpg', 'dark-s_py.jpg', 'dark-s_ny.jpg', 'dark-s_pz.jpg', 'dark-s_nz.jpg']
    };
  };
  Object.assign(THREE.SkyBox.prototype, {

    constructor: THREE.SkyBox,

    update: function (fileName) {

      if (!fileName) {
        fileName = "skyboxsun25deg"
      }

      var loader = new THREE.CubeTextureLoader();
      loader.setPath(this.baseFileUrl + fileName + '/');
      this.scene.background = loader.load(this.cubePic[fileName]);
    }
  });

  /**
   * 对门的控制
   */
  THREE.DoorController = function () {
  };
  Object.assign(THREE.DoorController.prototype, {
    /**
     * 执行开关们的操作
     * @param object 门的对象
     * @param doorState{Boolean} 控制门的开关状态 true 开门 false 关门 默认true
     */
    controlDoor: function (object, doorState) {
      if (object === undefined || object === null) return;
      if (doorState === undefined || doorState === null) doorState = true;

      var An_type = null;
      if (object.userData.instance.Annotation) {
        An_type = object.userData.instance.Annotation.dir;
      }

      // 查找门Group
      var doorGroup;
      if (object.constructor === THREE.Object3D) {
        doorGroup = object.children[0];
      }
      else if (object.constructor === THREE.Group) {
        doorGroup = object;
      }

      for (var i = 0; i < doorGroup.children.length; i++) {
        var doorCorner = new THREE.Vector3(0, 0, Math.PI / 2);
        var mesh = doorGroup.children[i];
        if (mesh.userData.type === "N") {
          continue;
        }
        var me_type = null;
        if (mesh.userData.type === "L") {
          me_type = "L";
        } else {
          me_type = "R";
        }
        var type = An_type || me_type;
        if (mesh.userData.openDoor === undefined) {
          mesh.userData.openDoor = false;
        }

        var RotatingObject = new THREE.RotatingObject(mesh);
        if (mesh.userData.openDoor !== doorState) {
          // 单个门
          if (type === "R") {
            var min = RotatingObject.getBoundingBoxMin().clone();
            var max = RotatingObject.getBoundingBoxMax().clone();
            var vec = min.clone();
            vec.x = max.x;
            vec.y = max.y;

            RotatingObject.setOrigin(vec);
            doorCorner = new THREE.Vector3(0, 0, -Math.PI / 2);
          }

          if (type === "L") {

            // 双开门
            if (mesh.userData.instance.FamilyAnnotation === "Mensk") {
              var min = RotatingObject.getBoundingBoxMin().clone();
              var max = RotatingObject.getBoundingBoxMax().clone();
              var vec = min.clone();

              vec.y = max.y;
              vec.z = max.z;

              doorCorner = new THREE.Vector3(0, 0, Math.PI / 2);
              RotatingObject.setOrigin(vec);
            }
          }

          if (mesh.userData.openDoor) {
            doorCorner = mesh.userData.doorCorner.negate();
            RotatingObject.rotate(doorCorner);
            mesh.userData.openDoor = false;
          } else {
            RotatingObject.rotate(doorCorner);
            mesh.userData.doorCorner = doorCorner;
            mesh.userData.openDoor = true;
          }
        }
      }
    }
  });

  /**
   * 射线的相关方法
   * @param camera：相机
   * @param scene：场景
   * @param options = {
   *    domElement: 操作的dom结构
   *    objects{Array}： 射线查找的物体 scene.children
   *    enableDetection{Boolean}：设置是否启动碰撞检测 false
   *    enabledChangeMaterialColor{Boolean}：控制碰撞检测是否显示颜色 false
   *    enabledClick{Boolean}：控制是否开启点选功能
   *  }
   * @constructor
   */
  THREE.RayControls = function (camera, scene, options) {

    // 初始化参数
    options = options || {};
    this.camera = camera; // 相机
    this.scene = scene; // 场景
    this.domElement = (options.domElement !== undefined) ? options.domElement : document; // 事件绑定的dom，默认为整个文档
    this.objects = options.objects !== undefined ? options.objects : this.scene.children; // 射线查找的物体
    this.enableDetection = options.enableDetection !== undefined ? options.enableDetection : false; // 设置启动碰撞检测

    // 鼠标事件相关属性
    // 常量值
    this.MOUSEDOWN = "mouseDown";
    this.MOUSEMOVE = "mouseMove";
    this.MOUSEDOWNMOVE = "mouseDownMove"; // 按下鼠标并移动
    this.MOUSEDOWNNOMOVE = "mouseDownNoMove"; // 按下鼠标没有移动
    this.MOVEFORWERD = 'moveForward';
    this.MOVEBACKWARD = 'moveBackward';
    this.MOUSENO = "mouseNo";
    this.moveForward = false;
    this.moveBackward = false;

    this.mouseMoveDirection = this.MOUSENO; // 记录鼠标移动的方向
    this.mouseState = this.MOUSENO; // 记录鼠标的状态
    this.mouseX = 0; // 鼠标的位置
    this.mouseY = 0;

    this.mouse = new THREE.Vector2(); // threejs中的标准坐标

    // this.detectionData = {
    //   changColor: new THREE.Color(0x00ff00),
    //   preMaterials: [] // 保存所有选中的材质
    // };
    //

    // 拾取物体
    this.raycaster = new THREE.Raycaster();
    this.filterMesh = false; // 射线拾取只返回mesh
    this.filterX = null; // 射线拾取时，返回小于filterX值的项
    this.filterY = null; // 射线拾取时，返回小于filterY值的项
    this.filterZ = null; // 射线拾取时，返回小于filterZ值的项

    // 点选物体所用到的参数
    this.enabledClick = options.enabledClick !== undefined ? options.enabledClick : true; // 是否启用点选功能
    // 选中改变材质所用的参数
    this.selectMaterialData = {
      changHexColor: new THREE.Color(0xff0000).getHex(), // 标记选中的颜色
      // currentObject: null, // 保存前选中的对象
      preMaterials: [] // 保存所有选中的材质
    };
    // 选中改变整个mesh所用的参数
    this.selectMeshData = {
      changHexColor: new THREE.Color(0xff0000).getHex(), // 标记选中的颜色
      preMesh: null // 保存所有选中的材质
    };
    this.detectionData = {
      changHexColor: new THREE.Color(0x00ff00).getHex(), // 标记选中的颜色
      preMesh: null // 保存所有选中的材质
    };
    this.detectionChangeColor = new THREE.Color(0x00ff00); // 碰撞检测设置的颜色

    // this.elementspropertys = options.elementspropertys !== undefined ? options.elementspropertys : null; // 数据中包含门的信息
    //
    // // 漫游初始化视点
    // this.roamSelectColor = new THREE.Color(0x00ff00);
    // this.roamChangOpacity = 0.5;
    // this.roamChangMaterial = false; // 记录漫游修改材质

    // this.dataDictionary = options.dataDictionary !== undefined ? options.dataDictionary : new THREE.DataDictionary(this.elementspropertys, this.scene, {});

    // 添加事件
    this.initEvent();
  };
  Object.assign(THREE.RayControls.prototype, {
    /**
     * 重置属性
     * @param options 重置参数
     *  options = {
     *    camera：相机
     *    scene：场景
     *    domElement: 操作的dom结构
     *    objects{Array}： 射线查找的物体 scene.children
     *    enableDetection{Boolean}：设置是否启动碰撞检测 false
     *    enabledChangeMaterialColor{Boolean}：控制碰撞检测是否显示颜色 false
     *    enabledClick{Boolean}：控制是否开启点选功能
     *  }
     *
     */
    reset: function (options) {
      options = options || {};
      if (options.camera !== undefined) {
        this.camera = options.camera;
      }
      if (options.scene !== undefined) {
        this.scene = options.scene;
      }
      if (options.domElement !== undefined) {
        this.domElement = options.domElement;
      }
      if (options.objects !== undefined) {
        this.objects = options.objects;
      }
      if (options.enableDetection !== undefined) {
        this.enableDetection = options.enableDetection;
      }
    },

    // /**
    //  * 更新属性elementspropertys
    //  * @param elementspropertys
    //  */
    // updateElementsPropertys: function (elementspropertys) {
    //   this.elementspropertys = elementspropertys;
    //   this.dataDictionary.elementspropertys = elementspropertys;
    // },
    //
    /*********************** 启动碰撞检测**********************************/
    /**
     * 启动碰撞检测
     * materialControl:材质控制对象
     * @returns {boolean} false 没有碰撞 true 发生碰撞
     */
    detection: function (objects, materialControl) {
      if (this.enableDetection && (this.mouseState === this.MOUSEDOWNMOVE)) {
        var direction = this.MOUSENO;
        if (!this.moveForward && this.moveBackward) {
          direction = this.MOVEBACKWARD;
        }
        if (this.moveForward && !this.moveBackward) {
          direction = this.MOVEFORWERD;
        }
        var intersects = this._intersectObjects(objects, direction, {
          filterMesh: false
        });

        // 修改选中的颜色
        if (materialControl) {
          var mesh = this._findFirstMesh(intersects);
          materialControl.clickChangeColor([mesh], this.detectionChangeColor);
        }

        if (intersects.length > 0) {
          if (intersects[0].distance <= 2) {
            return true;
          }
        }
      }
      return false;
    },

    // /*********************** 控制开关门的相关操作**************************/
    // /**
    //  * 实现开关们方法，事件绑定的方法
    //  * @private
    //  */
    // _controlDoor: function () {
    //   if (this.selectMaterialData.currentObject != null) {
    //
    //     var materialIndex = this.selectMaterialData.currentObject.face.materialIndex;
    //     var mesh = this.selectMaterialData.currentObject.object;
    //
    //     var searchObject = new THREE.DataDictionary(this.elementspropertys);
    //     var elementsPropertyObject = searchObject.findElementsPropertyObject(materialIndex, mesh);
    //
    //     if (elementsPropertyObject['类型'] === "OST_Doors") {
    //       this.controlDoor(mesh, elementsPropertyObject['注释'])
    //     }
    //   }
    // },
    // /**
    //  * 控制是否开关们
    //  * @param val{Boolean}：默认 false 不进行开关门的操作， true 可以开关们
    //  */
    // _enableControllerDoor: function (val) {
    //   if (val) {
    //     window.addEventListener('keydown', this._onKeyDown, false);
    //   } else {
    //     window.removeEventListener('keydown', this._onKeyDown, false);
    //   }
    // },
    // /**
    //  * 突出显示所有的灯
    //  */
    // setInitCameraByLights: function () {
    //   var objects = {};
    //   var elementsPropertyObjects = this.dataDictionary.findElementsPropertyObjectsByType("OST_LightingFixtures");
    //   for (var i = 0; i < elementsPropertyObjects.length; i++) {
    //     var mesh = this.dataDictionary.getMesh(elementsPropertyObjects[i].mesh_UUID);
    //     var materialIndexes = this.dataDictionary.getMaterialIndexes(mesh, elementsPropertyObjects[i].ID);
    //     if (objects[mesh.uuid] === undefined) {
    //       objects[mesh.uuid] = {mesh: mesh, materialIndexes: []}
    //     }
    //     for (var j = 0; j < materialIndexes.length; j++) {
    //       objects[mesh.uuid].materialIndexes.push(materialIndexes[j]);
    //     }
    //   }
    //   this.changeMeshMaterials(objects);
    // },
    //
    /************************ 点击事件相关方法*****************************/
    /**
     * 点击事件，改变选中几何体的颜色
     * @param event
     * @private
     */
    _selectObjects: function (event) {
      this._convertCoordinate(event);
      var intersects = this._intersectObjects(this.objects, this.MOVEFORWERD, {
        filterMesh: this.filterMesh,
        filterX: this.filterX,
        filterY: this.filterY,
        filterZ: this.filterZ
      });
      // var elementsObject = null;
      // var RevitID = null;

      if (intersects.length > 0) {

        // 修改选中材质的颜色
        // var material = this._findFirstMaterial(intersects);
        // this._changeMaterials([material], this.selectMaterialData);
        // 修改选中网格的颜色
        var mesh = this._findFirstMesh(intersects);
        // this._changeMeshMaterials(mesh,this.selectMeshData)

        // RevitID = material.userData.RevitID;
        // elementsObject = material.userData;
        // this.selectMaterialData.currentObject = intersects[0];

        // var mesh = this._findFirstMesh(intersects);
      } else {
        // this.selectMaterialData.currentMesh = null;
      }

      // 调用返回函数
      if (this.selectObjectsCallBack) {
        this.selectObjectsCallBack(mesh);
      }

      // elementsObject = null;
    },
    /**
     * 控制点选时，是否使用x,y,x值进行过滤
     * @param type{String} 类型 {X、Y、Z}
     * @param number{int} 如果number为null，则取消过滤
     */
    filterXYZ: function (type, number) {
      if ("X" === type.toLocaleUpperCase()) {
        this.filterX = number;
      }

      if ("Y" === type.toLocaleUpperCase()) {
        this.filterY = number;
      }

      if ("Z" === type.toLocaleUpperCase()) {
        this.filterZ = number;
      }
    },

    /************************ 计算鼠标位置*********************************/
    /**
     * 将鼠标位置的屏幕坐标转成threejs中的标准坐标
     * @param event
     * @private
     */
    _convertCoordinate: function (event) {
      var left = this._getOffsetLeft(this.domElement);
      var top = this._getOffsetTop(this.domElement);
      this.mouse.x = ((event.clientX - left) / this.domElement.offsetWidth) * 2 - 1;
      this.mouse.y = -((event.clientY - top) / this.domElement.offsetHeight) * 2 + 1;
    },
    /**
     * 获取元素obj到左边的距离
     * @param obj
     * @returns {number}
     * @private
     */
    _getOffsetLeft: function (obj) { //获取某元素以浏览器左上角为原点的坐标
      var l = obj.offsetLeft; //对应父容器的上边距
      //判断是否有父容器，如果存在则累加其边距
      while (obj = obj.offsetParent) {//等效 obj = obj.offsetParent;while (obj != undefined)
        l += obj.offsetLeft; //叠加父容器的左边距
      }
      return l;
    },
    /**
     * 获取元素obj到上边的距离
     * @param obj
     * @returns {number}
     * @private
     */
    _getOffsetTop: function (obj) { //获取某元素以浏览器左上角为原点的坐标
      var t = obj.offsetTop; //获取该元素对应父容器的上边距
      //判断是否有父容器，如果存在则累加其边距
      while (obj = obj.offsetParent) {//等效 obj = obj.offsetParent;while (obj != undefined)
        t += obj.offsetTop; //叠加父容器的上边距
      }
      return t;
    },

    /************************ 射线检测相关的方法***************************/
    /**
     * 射线查找物体
     * @param objects：查找的数组
     * @param direction：射线的方向
     * @param option：控制可选参数
     *  filterMesh：是否过滤出mesh
     *  filterX：过滤x值大小
     *  filterY：过滤y值大小
     *  filterZ：过滤z值大小
     * @returns {Array}
     * @private
     */
    _intersectObjects: function (objects, direction, option) {
      option = option || {};
      var result = [];

      // 控制射线方向
      if (direction === this.MOUSENO) {
        return result;
      }
      else if (direction === this.MOVEFORWERD) {
        this.raycaster.setFromCamera(this.mouse, this.camera);
      }
      else if (direction === this.MOVEBACKWARD) {
        var origin = new THREE.Vector3();
        origin.setFromMatrixPosition(this.camera.matrixWorld);
        var end = new THREE.Vector3();
        end.set(this.mouse.x, this.mouse.y, 0.5).unproject(this.camera);
        this.raycaster.set(origin, end.sub(origin).negate().normalize());
      }

      var intersects = this.raycaster.intersectObjects(objects, true);

      if (intersects.length > 0) {
        if (option.filterMesh || option.filterX || option.filterY || option.filterZ) {
          for (var i = 0; i < intersects.length; i++) {
            if (!option.filterMesh || (option.filterMesh && intersects[i].object instanceof THREE.Mesh)) {
              var isFilter = false;
              if (option.filterX && (intersects[i].point.x > option.filterX)) {
                isFilter = true;
              }
              if (option.filterY && (intersects[i].point.y > option.filterY)) {
                isFilter = true;
              }
              if (option.filterZ && (intersects[i].point.z > option.filterZ)) {
                isFilter = true;
              }

              if (!isFilter) {
                result.push(intersects[i]);
              }
            }
          }
        } else {
          result = intersects;
        }
      }

      if (result.length < 0) result = null;
      return result;
    },
    /**
     * 根据射线查找结果，获取第一个有效的材质
     * @param intersects
     * @return {null}
     * @private
     */
    _findFirstMaterial: function (intersects) {
      for (var i = 0; i < intersects.length; i++) {
        var intersected = intersects[i].object;
        if (intersected.constructor === THREE.Mesh) {
          var materialIndex = intersects[i].face.materialIndex;
          return intersected.material[materialIndex];
        }
      }

      return null;
    },
    /**
     * 根据射线查找结果，获取第一个有效的网格
     * @param intersects
     * @return {null}
     * @private
     */
    _findFirstMesh: function (intersects) {
      for (var i = 0; i < intersects.length; i++) {
        if (intersects[i].object.constructor === THREE.Mesh) {
          return intersects[i].object;
        }
      }
      return null;
    },

    /************************ 修改材质******************************/
    // changeMeshMaterials: function (objects) {
    //   var i, j;
    //   for (i = 0; i < this.scene.children.length; i++) {
    //     if (this.scene.children[i].constructor === THREE.Mesh) {
    //       var mesh = this.scene.children[i];
    //       for (var key in objects) {
    //         var object = objects[key];
    //
    //         for (j = 0; j < mesh.material.length; j++) {
    //           if (mesh.uuid === key && object.materialIndexes.indexOf(j) > -1) {
    //             mesh.material[j].userData.roamChangColor = true;
    //             mesh.material[j].userData.originColor = mesh.material[j].color;
    //             mesh.material[j].color = this.roamSelectColor;
    //           } else {
    //             mesh.material[j].userData.roamChangTransparent = true;
    //             mesh.material[j].userData.originTransparent = mesh.material[j].transparent;
    //             mesh.material[j].userData.originOpacity = mesh.material[j].opacity;
    //
    //             mesh.material[j].opacity = this.roamChangOpacity;
    //             mesh.material[j].transparent = true;
    //           }
    //         }
    //       }
    //     }
    //   }
    //   this.roamChangMaterial = true;
    // },
    // /**
    //  * 通过材质的名称，修改材质的颜色
    //  * @param materialName:材质名称
    //  * @param scene:查找的范围
    //  */
    // changeMaterialByMaterialName: function (materialName, scene) {
    //   var currentMaterial = null;
    //
    //   // 查找对象的material
    //   for (var i = 0; i < scene.children.length; i++) {
    //     if (scene.children[i].constructor === THREE.Mesh) {
    //       for (var j = 0; j < scene.children[i].material.length; j++) {
    //         if (scene.children[i].material[j].name === materialName) {
    //           currentMaterial = scene.children[i].material[j];
    //           break;
    //         }
    //       }
    //     }
    //   }
    //   this._changeMaterial(currentMaterial, this.selectMaterialData);
    // },
    // /**
    //  * 通过RyevitID修改当前选中几何体材质颜色
    //  * @param RevitID
    //  */
    // changeMaterialByRyevitID: function (RevitID) {
    //   var mesh = null;
    //   var currentMaterials = [];
    //
    //   var elementsPropertyObject = this.dataDictionary.findElementsProperty(RevitID);
    //
    //   if (elementsPropertyObject) {
    //     mesh = this.dataDictionary.getMesh(elementsPropertyObject.mesh_UUID);
    //     currentMaterials = this.dataDictionary.getMaterials(mesh, elementsPropertyObject.ID);
    //   }
    //
    //   this._changeMaterials(currentMaterials, this.selectMaterialData);
    // },
    // /**
    //  * 改变选中材质的颜色值
    //  * 1. 如果当前选中的材质和之前选中的材质是不同的
    //  *  1.1 把上一个选中的材质，更换为原本的颜色
    //  *  1.2 记录当前材质的颜色
    //  *  1.3 改变当前材质的颜色
    //  * 2. 如果当前选中的颜色和原来选中的颜色值相同
    //  *  2.1 将材质颜色更换为原来的材质颜色
    //  *  2.2 将记录的颜色设置为null
    //  * @param currentMaterial：当前材质
    //  * @param data：数据域
    //  * @private
    //  * @deprecated
    //  */
    // _changeMaterial: function (currentMaterial, data) {
    //   var currentMaterials = [];
    //   if (currentMaterial) {
    //     currentMaterials.push(currentMaterial);
    //   }
    //   this._changeMaterials(currentMaterials, data);
    // },

    /**
     * 改变选中材质的颜色值
     * 1. 如果当前选中的材质和之前选中的材质是不同的
     *  1.1 把上一个选中的材质，更换为原本的颜色
     *  1.2 记录当前材质的颜色
     *  1.3 改变当前材质的颜色
     * 2. 如果当前选中的颜色和原来选中的颜色值相同
     *  2.1 将材质颜色更换为原来的材质颜色
     *  2.2 将记录的颜色设置为null
     * @param currentMaterials{Array}：当前材质
     * @param data：数据域
     * @private
     */
    _changeMaterials: function (currentMaterials, data) {
      var i;

      // 判断这次选中的材质和上次选中的材质是否相同
      var isSame = true;
      if (currentMaterials.length === data.preMaterials.length) {
        for (i = 0; i < currentMaterials.length; i++) {
          if (currentMaterials[i] !== data.preMaterials[i].preMaterial) {
            isSame = false;
            break;
          }
        }
      } else {
        isSame = false;
      }

      // 把上次选中的材质设置为原有的颜色
      if (data.preMaterials.length > 0) {
        for (i = 0; i < data.preMaterials.length; i++) {
          data.preMaterials[i].preMaterial.color.setHex(data.preMaterials[i].preMaterial.userData.source.hexColor);
        }
        data.preMaterials = [];
      }

      // 改变当前选中材质的颜色，记录当前材质信息
      if (!isSame) {
        for (i = 0; i < currentMaterials.length; i++) {
          currentMaterials[i].userData.source = currentMaterials[i].userData.source || {};
          if (!currentMaterials[i].userData.source.hexColor) {
            currentMaterials[i].userData.source.hexColor = currentMaterials[i].color.getHex();
          }
          currentMaterials[i].color.setHex(data.changHexColor);

          data.preMaterials.push({preMaterial: currentMaterials[i]});
        }
      }

      currentMaterials = null;
      data = null;
    },
    /**
     * 修改整个mesh的材质颜色
     * @param mesh
     * @param data
     * @private
     */
    _changeMeshMaterials: function (mesh, data) {

      // 把上次选中的材质设置为原有的颜色
      if (data.preMesh) {
        for (i = 0; i < data.preMesh.material.length; i++) {
          data.preMesh.material[i].color.setHex(data.preMesh.material[i].userData.source.hexColor);
        }
        data.preMesh = null;
      }

      // 改变选中网格的颜色
      if (mesh) {
        for (var i = 0; i < mesh.material.length; i++) {
          mesh.material[i].userData.source = mesh.material[i].userData.source || {};
          if (!mesh.material[i].userData.source.hexColor) {
            mesh.material[i].userData.source.hexColor = mesh.material[i].color.getHex();
          }
          mesh.material[i].color.setHex(data.changHexColor);
        }
        data.preMesh = mesh;
      }
    },

    /************************** 事件 *********************************************/
    initEvent: function () {
      this._onMouseMove = this._bind(this, this.onMouseMove);
      this._onMouseDown = this._bind(this, this.onMouseDown);
      this._onMouseUp = this._bind(this, this.onMouseUp);

      this.domElement.addEventListener('mousemove', this._onMouseMove, false);
      this.domElement.addEventListener('mousedown', this._onMouseDown, false);
      this.domElement.addEventListener('mouseup', this._onMouseUp, false);
    },

    _bind: function (scope, fn) {
      return function () {
        fn.apply(scope, arguments);
      };
    },

    onMouseDown: function (event) {
      event.preventDefault();
      // event.stopPropagation();
      // console.log("射线：onMouseDown");

      this.mouseState = this.MOUSEDOWN;
      this.mouseX = event.clientX;
      this.mouseY = event.clientY;

      if (this.enableDetection) {
        switch (event.button) {
          case 0:
            this.moveForward = true;
            break;
          case 2:
            this.moveBackward = true;
            break;
        }
      }
    },
    onMouseMove: function (event) {
      event.preventDefault();
      // event.stopPropagation();
      // console.log("射线：onMouseMove");

      // 设置鼠标状态
      if ((this.mouseState === this.MOUSEDOWN) || (this.mouseState === this.MOUSEDOWNMOVE)) {
        if (this.mouseX === event.clientX && this.mouseY === event.clientY) {
          this.mouseState = this.MOUSEDOWNNOMOVE;
        } else {
          this.mouseState = this.MOUSEDOWNMOVE;
        }
      } else {
        this.mouseState = this.MOUSEMOVE;
      }

      // if (this.enableDetection) {
      //   this._convertCoordinate(event);
      // }
    },
    onMouseUp: function (event) {
      event.preventDefault();
      // event.stopPropagation();
      // console.log("射线：onMouseUp");

      // 触发拾取功能
      if (this.enabledClick) {
        if (this.mouseState === this.MOUSEDOWNNOMOVE || this.mouseState === this.MOUSEDOWN) {
          this._selectObjects(event);
        }
      }

      this.mouseState = this.MOUSENO;

      if (this.enableDetection) {
        switch (event.button) {
          case 0:
            this.moveForward = false;
            break;
          case 2:
            this.moveBackward = false;
            break;
        }
      }
    },

    /**
     * 删除鼠标事件
     */
    dispose: function () {
      this.domElement.removeEventListener('mousedown', this._onMouseDown, false);
      this.domElement.removeEventListener('mouseup', this._onMouseMove, false);
      this.domElement.removeEventListener('mousemove', this._onMouseUp, false);
      // this.domElement.removeEventListener('keydown', this._onKeyDown, false);
    },

    /**************************** 返回函数 **********************************/
    /**
     * 点选的返回函数
     * @param mesh
     */
    selectObjectsCallBack: function (mesh) {
    }
  })

})(THREE);
(function (LR) {
  /**
   * 控制修改材质
   * @constructor
   */
  LR.MaterialController = function () {
    this.preMeshes = []; // 保存点选改变了的材质
    this.color = new THREE.Color(0xff0000); // 选中默认颜色
    this.changeOpacity = 0.5;
    this.isChangeAllMeshOpacity = false; // 记录是否修改了场景中mesh的透明度
  };
  Object.assign(LR.MaterialController, {
    // 定义改变材质颜色的类型
    COMMON_TYPE: 0, // 没有进行修改颜色
    ALARM_TYPE: 1, // 警报修改颜色
    LIGHT_TYPE: 2, // 开灯修改颜色
    CLICK_TYPE: 3, // 点选修改颜色

    // 定义颜色级别
    A_LEVEL: "A",
    B_LEVEL: "B",
    C_LEVEL: "C",
    D_LEVEL: "D",
    E_LEVEL: "E"
  });
  Object.assign(LR.MaterialController.prototype, {
    /****************** 改变材质颜色 ****************************************/
    /**
     * 警报更换颜色
     * @param mesh 机械对象
     * @param option
     *  level：级别
     *  color{THREE.Color}：颜色
     */
    alarmChangeMaterial: function (mesh, option) {
      var color;
      if (option.color) {
        color = option.color;
      } else {
        color = this._getLevelColor(option.level);
      }

      this._changeColorByType(mesh, color, LR.MaterialController.ALARM_TYPE)
    },

    /**
     * 点选修改mesh颜色
     * @param meshes{Array}
     * @param color{THREE.Color}
     * @param isInverseSelect{Boolean} : false 不反选 true 反选 默认反选
     */
    clickChangeColor: function (meshes, color, isInverseSelect) {
      // 检查传入参数
      if (color === undefined) {
        color = this.color;
      }
      if (isInverseSelect !== false) {
        isInverseSelect = true;
      }

      // 判断当前选中的是否和前一次选中的一样
      var isChangCommon = this._isClickChangeCommon(meshes);

      // 恢复上一个选中的meshes
      if (!(isChangCommon && !isInverseSelect)) {
        this._restoreMeshesMaterialColor();
      }

      if (!isChangCommon) {

        for (var i = 0; i < meshes.length; i++) {
          this._changeColorByType(meshes[i], color, LR.MaterialController.CLICK_TYPE);
        }

        // 记录当前改变的材质
        this.preMeshes = meshes;
      }
    },

    /**
     * 开灯
     * @param mesh
     * @param materialIndex 灯泡id
     */
    lightChangeColor: function (mesh, materialIndex) {
      this._changeColorByType(mesh, null, LR.MaterialController.LIGHT_TYPE, materialIndex);
    },
    /**
     * 关灯时，修改材质
     * @param mesh
     */
    closeLightChange: function (mesh) {
      if (mesh.userData.TYPE === LR.MaterialController.LIGHT_TYPE) {
        mesh.material = mesh.userData.sourceMaterial;
      }

      // 删除亮灯的材质颜色
      if (mesh.userData.openedLightMaterial) {
        this._disposeMaterial(mesh.userData.openedLightMaterial);
      }

      mesh.userData.isOpenedLight = false;
    },

    /**
     * 改变网格材质颜色
     * @param mesh 待修改的网格
     * @param color 材质修改的颜色
     * @param type 改变类型
     * @param materialIndex
     */
    _changeColorByType: function (mesh, color, type, materialIndex) {
      if (color === undefined) {
        color = this.color;
      }

      var currentMaterial;

      // 保存当前材质信息
      this._saveCurrentMaterial(mesh);

      if (type === LR.MaterialController.LIGHT_TYPE) {
        // 修改灯泡材质
        var currentMaterials = [];
        for (var i = 0; i < mesh.material.length; i++) {
          if (i === materialIndex) {
            currentMaterial = mesh.userData.sourceMaterial[i].clone();

            currentMaterial.color = new THREE.Color(0xffffff);
            currentMaterial.emissive = new THREE.Color(0xece967);
            currentMaterial.emissiveIntensity = 200;
            currentMaterial.opacity = 0.5;
            currentMaterial.transparent = true;

            currentMaterials.push(currentMaterial);
          } else {
            currentMaterials.push(mesh.userData.sourceMaterial[i]);
          }
        }

        if (mesh.userData.isClick || mesh.userData.isAlarm) {
          mesh.userData.openedLightMaterial = currentMaterials;
        } else {
          mesh.material = currentMaterials;
          mesh.userData.TYPE = type;
        }

        mesh.userData.isOpenedLight = true;
      }
      else {
        // 警报类型 点选类型
        var newMaterial = [];
        if (Array.prototype.isArrayFn(mesh.material)) {
          for (var j = 0; j < mesh.material.length; j++) {
            currentMaterial = mesh.userData.sourceMaterial[j].clone();
            currentMaterial.color = color;
            newMaterial.push(currentMaterial);
          }
        } else {
          currentMaterial = mesh.userData.sourceMaterial.clone();
          currentMaterial.color = color;
          newMaterial = currentMaterial;
        }
      }

      // 警报类型
      if (type === LR.MaterialController.ALARM_TYPE) {
        if (mesh.userData.isClick) {
          mesh.userData.AlarmMaterial = newMaterial;
        } else {
          mesh.material = newMaterial;
          mesh.userData.TYPE = type;
        }

        mesh.userData.isAlarm = true;
      }
      // 点选类型
      if (type === LR.MaterialController.CLICK_TYPE) {
        mesh.material = newMaterial;
        mesh.userData.isClick = true;
        mesh.userData.TYPE = type;
      }
    },
    /**
     * 保存当前材质信息
     * @param mesh
     * @private
     */
    _saveCurrentMaterial: function (mesh) {
      // 保存原始材质信息
      if (mesh.userData.TYPE === LR.MaterialController.CLICK_TYPE) {
        if (!mesh.userData.clickMaterial) mesh.userData.clickMaterial = mesh.material;
      } else if (mesh.userData.TYPE === LR.MaterialController.LIGHT_TYPE) {
        if (!mesh.userData.openedLightMaterial) mesh.userData.openedLightMaterial = mesh.material;
      } else if (mesh.userData.TYPE === LR.MaterialController.ALARM_TYPE) {
        if (!mesh.userData.AlarmMaterial) {
          mesh.userData.AlarmMaterial = mesh.material;
        }
      } else {
        if (!mesh.userData.sourceMaterial) mesh.userData.sourceMaterial = mesh.material;
      }
    },
    /**
     * 获取级别对应的颜色对象
     * @param level 级别 A\B\C\D\E
     * @private
     */
    _getLevelColor: function (level) {
      if (level === LR.MaterialController.A_LEVEL) {
        return new THREE.Color(0xf39606);
      } else if (level === LR.MaterialController.B_LEVEL) {
        return new THREE.Color(0xff0000);
      } else if (level === LR.MaterialController.C_LEVEL) {
        return new THREE.Color(0x0000ff);
      } else if (level === LR.MaterialController.D_LEVEL) {
        return new THREE.Color(0x00ffff);
      } else if (level === LR.MaterialController.E_LEVEL) {
        return new THREE.Color(0xffff00);
      }
    },
    /**
     * 恢复上次点选改变的材质颜色
     */
    _restoreMeshesMaterialColor: function () {
      for (var i = 0; i < this.preMeshes.length; i++) {
        if (this.preMeshes[i].userData.TYPE === LR.MaterialController.CLICK_TYPE) {
          if (this.preMeshes[i].userData.isAlarm) {
            this.preMeshes[i].material = this.preMeshes[i].userData.AlarmMaterial;
            this.preMeshes[i].userData.TYPE = LR.MaterialController.ALARM_TYPE;
          }
          else if (this.preMeshes[i].userData.isOpenedLight) {
            this.preMeshes[i].material = this.preMeshes[i].userData.openedLightMaterial;
            this.preMeshes[i].userData.TYPE = LR.MaterialController.LIGHT_TYPE;
          }
          else {
            this.preMeshes[i].material = this.preMeshes[i].userData.sourceMaterial;
            this.preMeshes[i].userData.TYPE = LR.MaterialController.COMMON_TYPE;
          }
          this.preMeshes[i].userData.isClick = false;
        }
        this._disposeMaterial(this.preMeshes[i].userData.clickMaterial);
        delete this.preMeshes[i].userData.clickMaterial;
      }

      this.preMeshes = [];
    },
    /**
     * 删除材质
     */
    _disposeMaterial: function (materials) {
      if (materials === undefined) return;
      if (Array.prototype.isArrayFn(materials)) {
        for (var i = 0; i < materials.length; i++) {
          if (materials[i].map) {
            materials[i].map.dispose();
          }
          materials[i].dispose();
        }
      } else {
        if (materials.map) {
          materials.map.dispose();
        }
        materials.dispose();
      }
    },
    /**
     * 判断当前选中颜色和上一个选中颜色是否一致
     * @param arr{Array}
     * @return {boolean} 相同 true 不同 false
     * @private
     */
    _isClickChangeCommon: function (arr) {
      var i;
      if (this.preMeshes.length !== arr.length) {
        return false;
      }
      else {
        for (i = 0; i < arr.length; i++) {
          if (this.preMeshes[i] !== arr[i]) {
            return false;
          }
        }
      }

      return true;
    },
    /****************** 改变材质透明度 ****************************************/
    /**
     * 改变object中所有除meshes中网格的透明度
     * @param object 透明的范围
     * @param meshes 网格
     * @param changOpacity 透明度值
     */
    changeOtherMeshesOpacity: function (object, meshes, changOpacity) {

      changOpacity = changOpacity || this.changeOpacity;

      searchObject(object, changOpacity);
      this.isChangeAllMeshOpacity = true;

      function searchObject(object, changOpacity) {
        var i;
        var isChangeOpacity = true;
        if (object.constructor === THREE.Mesh) {
          for (i = 0; i < meshes.length; i++) {
            if (object === meshes[i]) {
              isChangeOpacity = false;
            }
          }

          if (Array.prototype.isArrayFn(object.material)) {
            for (i = 0; i < object.material.length; i++) {

              if (isChangeOpacity) {
                object.material[i].userData.originTransparent = object.material[i].userData.originTransparent || object.material[i].transparent;
                object.material[i].userData.originOpacity = object.material[i].userData.originOpacity || object.material[i].opacity;

                object.material[i].opacity = changOpacity;
                object.material[i].transparent = true;
              } else {
                if (object.material[i].userData.originTransparent) {
                  object.material[i].transparent = object.material[i].userData.originTransparent;
                }
                if (object.material[i].userData.originOpacity) {
                  object.material[i].opacity = object.material[i].userData.originOpacity;
                }
              }
            }
          } else {
            if (isChangeOpacity) {
              object.material.userData.originTransparent = object.material.userData.originTransparent || object.material.transparent;
              object.material.userData.originOpacity = object.material.userData.originOpacity || object.material.opacity;

              object.material.opacity = changOpacity;
              object.material.transparent = true;
            } else {
              if (object.material.userData.originTransparent) {
                object.material.transparent = object.material.userData.originTransparent;
              }
              if (object.material.userData.originOpacity) {
                object.material.opacity = object.material.userData.originOpacity;
              }
            }
          }
        }
        else if (object.constructor === THREE.Object3D || object.constructor === THREE.Group) {
          for (i = 0; i < object.children.length; i++) {
            searchObject(object.children[i], changOpacity)
          }
        }
      }
    },
    /**
     * 取消透明设置
     */
    restoreAllMeshOpacity: function (object) {
      if (this.isChangeAllMeshOpacity) {
        searchObject(object);
        this.isChangeAllMeshOpacity = false;
      }

      function searchObject(object) {
        var i;
        if (object.constructor === THREE.Mesh) {
          if (Array.prototype.isArrayFn(object.material)) {

            for (i = 0; i < object.material.length; i++) {
              if (object.material[i].userData.originOpacity) {
                object.material[i].opacity = object.material[i].userData.originOpacity;
                object.material[i].transparent = object.material[i].userData.originTransparent;

                if (object.userData.TYPE === LR.MaterialController.ALARM_TYPE) {
                  object.userData.AlarmMaterial[i].opacity = object.userData.AlarmMaterial[i].userData.originOpacity;
                  object.userData.AlarmMaterial[i].transparent = object.userData.AlarmMaterial[i].userData.originTransparent;
                }
              }
            }
          }
          else {
            if (object.material.userData.originOpacity) {
              object.material.opacity = object.material.userData.originOpacity;
              object.material.transparent = object.material.userData.originTransparent;

              if (object.userData.TYPE === LR.MaterialController.ALARM_TYPE) {
                object.userData.AlarmMaterial.opacity = object.AlarmMaterial.userData.originOpacity;
                object.userData.AlarmMaterial.transparent = object.AlarmMaterial.userData.originTransparent;
              }
            }
          }
        }
        else if (object.constructor === THREE.Object3D || object.constructor === THREE.Group) {
          for (i = 0; i < object.children.length; i++) {
            searchObject(object.children[i])
          }
        }
      }
    }
  });

  /**
   * 控制修改灯光
   * @param scene
   * @param option
   * @constructor
   */
  LR.LightShadowControls = function (scene, option) {
    option = option || {};
    this.scene = scene;

    this.lightData = {}; // 保存灯光结构数据

    // // 光源位置
    // this.pos = option.pos !== undefined ? option.pos : new THREE.Vector3(18.64, 9.2, -36.07);
    //
    // // 点光源灯光的颜色
    // this.bulbLightColor = option.bulbLightColor !== undefined ? option.bulbLightColor : new THREE.Color(0xe8dfa7);
    // this.bulbLightIntensity = option.bulbLightIntensity !== undefined ? parseInt(option.bulbLightIntensity) : 1;
    // this.bulbLightDistance = option.bulbLightDistance !== undefined ? parseInt(option.bulbLightDistance) : 50;
    // this.bulbLightDecay = option.bulbLightDecay !== undefined ? parseInt(option.bulbLightDecay) : 2;
    // // 光的力量
    // this.bulbPower = option.bulbPower !== undefined ? parseInt(option.bulbPower) : 0;
    // // 阴影贴图偏差，在决定表面是否处于阴影中时，从标准化深度加或减多少。默认值为0.此处的微小调整（按照0.0001的顺序）可能有助于减少阴影中的人为因素
    // this.bulbShadowBias = option.bulbShadowBias !== undefined ? parseFloat(option.bulbShadowBias) * 0.0001 : 0.0001;
    // this.bulbShadowMapSizeWidth = option.bulbShadowMapSizeWidth !== undefined ? parseFloat(option.bulbShadowMapSizeWidth) : 1024;
    // this.bulbShadowMapSizeHeight = option.bulbShadowMapSizeHeight !== undefined ? parseInt(option.bulbShadowMapSizeHeight) : 1024;
    // this.bulbShadowCameraNear = option.bulbShadowCameraNear !== undefined ? parseFloat(option.bulbShadowCameraNear) : 0.5;
    // this.bulbShadowCamerafar = option.bulbShadowCamerafar !== undefined ? parseFloat(option.bulbShadowCamerafar) : 50;
    // this.bulbShadowRadius = option.bulbShadowRadius !== undefined ? parseFloat(option.bulbShadowRadius) : 5;
    //
    // // 放置所有灯
    // this.bulbLights = [];
    //
    // 聚光灯属性
    this.spotLightColor = option.spotLightColor !== undefined ? option.spotLightColor : new THREE.Color(0xc8a961);
    this.spotLightIntensity = option.spotLightIntensity !== undefined ? parseFloat(option.spotLightIntensity) : 14;
    this.spotLightDistance = option.spotLightDistance !== undefined ? parseFloat(option.spotLightDistance) : 100;
    this.spotLightAngle = option.spotLightAngle !== undefined ? parseFloat(option.spotLightAngle) : 0.9;
    this.spotLightPenumbra = option.spotLightPenumbra !== undefined ? parseFloat(option.spotLightPenumbra) : 1;
    this.spotLightDecay = option.spotLightDecay !== undefined ? parseFloat(option.spotLightDecay) : 0;
    this.openedLight = {}; // 记录开启的灯光
    // this.spotLights = []; // 测试灯光

    // 太阳光
    this.sunColor = option.sunColor !== undefined ? option.sunColor : new THREE.Color(0xd4ccb2);
    this.sunIntensity = option.sunIntensity !== undefined ? option.sunIntensity : 10;
    this.sunLight = null;

    // 平行光
    this.directionalLightColor = option.directionalLightColor !== undefined ? option.directionalLightColor : new THREE.Color(0xebe2c7);
    this.directionalLightIntensity = option.directionalLightIntensity !== undefined ? option.directionalLightIntensity : 1;
    this.directionalLight = null;
  };
  Object.assign(LR.LightShadowControls.prototype, {

    /**
     * 恢复到初始化状态
     */
    reset: function () {
      delete this.openedLight.currentDistribution;
      delete this.openedLight.currentLevel;
      delete this.openedLight.currentRoom;
      this.closeAllLight();

      this.lightData = {}; // 保存灯光结构数据
      this.openedLight = {}; // 记录开启的灯光
    },

    // addPointLight: function (pos, mesh) {
    //   var lightPos = pos !== undefined ? pos : this.pos;
    //   var lightMesh = mesh;
    //
    //   if (!lightMesh) {
    //     var bulbGeometry = new THREE.SphereGeometry(0.2, 16, 8);
    //     var bulbMat = new THREE.MeshStandardMaterial({
    //       emissive: 0xffffee,
    //       emissiveIntensity: 1,
    //       color: 0x000000
    //     });
    //     lightMesh = new THREE.Mesh(bulbGeometry, bulbMat);
    //   }
    //
    //   // 测试光源
    //   var bulbLight = new THREE.PointLight(this.bulbLightColor, this.bulbLightIntensity, this.bulbLightDistance, this.bulbLightDecay);
    //   bulbLight.power = this.bulbPower;
    //   bulbLight.position.set(lightPos.x, lightPos.y, lightPos.z);
    //   bulbLight.add(lightMesh);
    //
    //   // 开启灯的阴影
    //   bulbLight.castShadow = true;
    //   // this.bulbLight.shadow = new THREE.LightShadow(new THREE.PerspectiveCamera(60, this.container.offsetWidth / this.container.offsetHeight, 5, 50));
    //   bulbLight.shadow.bias = this.bulbShadowBias;
    //   bulbLight.shadow.mapSize.width = this.bulbShadowMapSizeWidth;
    //   bulbLight.shadow.mapSize.height = this.bulbShadowMapSizeHeight;
    //   bulbLight.shadow.camera.near = this.bulbShadowCameraNear;
    //   bulbLight.shadow.camera.far = this.bulbShadowCamerafar;
    //   bulbLight.shadow.radius = this.bulbShadowRadius;
    //
    //   this.bulbLights.push(bulbLight);
    //   this.scene.add(bulbLight);
    // },
    addSunLight: function (pos, option) {
      option = option || {};
      var lightPos = pos !== undefined ? pos : this.pos;
      var sunShadows = option.sunShadows !== undefined ? option.sunShadows : false;

      var sunLight = new THREE.PointLight(this.sunColor, this.sunIntensity, 0, 0);
      sunLight.position.set(lightPos.x, lightPos.y, lightPos.z);
      sunLight.castShadow = sunShadows;

      if (option.addBulb) {
        var bulbGeometry = new THREE.SphereGeometry(2, 16, 8);
        var bulbMat = new THREE.MeshStandardMaterial({
          emissive: 0xffffee,
          emissiveIntensity: 100,
          color: 0xf9f4c2
        });
        var lightMesh = new THREE.Mesh(bulbGeometry, bulbMat);
        sunLight.add(lightMesh);
      }

      this.sunLight = sunLight;
      this.scene.add(sunLight);
    },
    addDirectionalLight: function (pos, option) {
      option = option || {};
      var lightPos = pos !== undefined ? pos : this.pos;

      // 测试光源
      var directionalLight = new THREE.DirectionalLight(this.directionalLightColor, this.directionalLightIntensity);
      directionalLight.position.set(lightPos.x, lightPos.y, lightPos.z);

      if (option.addBulb) {
        var bulbGeometry = new THREE.SphereGeometry(2, 16, 8);
        var bulbMat = new THREE.MeshStandardMaterial({
          emissive: 0xff0e00,
          emissiveIntensity: 100,
          color: 0xf9f4c2
        });
        var lightMesh = new THREE.Mesh(bulbGeometry, bulbMat);
        directionalLight.add(lightMesh);
      }

      this.directionalLight = directionalLight;
      this.scene.add(directionalLight);
    },

    /**
     * 初始化灯的数据 this.lightData
     */
    initLightData: function () {
      this.lightData = this.lightData || {};

      search(this.scene, this.lightData);

      function search(object, lightData) {
        for (var i = 0; i < object.children.length; i++) {
          if (object.children[i].constructor === THREE.Mesh) {
            var mesh = object.children[i];
            if (mesh.userData.instance && mesh.userData.instance.BuiltInCategory === "OST_LightingFixtures") {

              var Annotation = LR.getAnnotation(object);
              if (Annotation.distribution) {
                lightData[Annotation.distribution] = lightData[Annotation.distribution] || {}; // 配电箱
                lightData[Annotation.distribution][Annotation.room] = lightData[Annotation.distribution][Annotation.room] || {}; // 房间 A
                lightData[Annotation.distribution][Annotation.room][Annotation.region] = lightData[Annotation.distribution][Annotation.room][Annotation.region] || {}; // 区域 B
                lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop] = lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop] || {}; // 电路 c
                lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel] = lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel] || {}; // 面板
                lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel][Annotation.button] = lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel][Annotation.button] || {}; // 按钮 D
                lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel][Annotation.button][mesh.userData.instance.id] = lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel][Annotation.button][mesh.userData.instance.id] || {}; // 灯泡

                for (var j = 0; j < mesh.material.length; j++) {
                  if (mesh.material[j].name.indexOf('灯泡') > -1) {
                    // 灯泡上的数据
                    var bulb = lightData[Annotation.distribution][Annotation.room][Annotation.region][Annotation.loop][Annotation.panel][Annotation.button][mesh.userData.instance.id];
                    bulb.materialIndex = j;
                    bulb.level = Annotation.level;
                    bulb.mesh = mesh;
                  }
                }
              } else {
                // console.log("灯光注释数据不正确！", mesh);
              }
            }
          }
          else if (object.children[i].constructor === THREE.Object3D) {
            search(object.children[i], lightData);
          }
        }
      }
    },
    /**
     * 查找要开启的灯泡,如果一个按钮上没有灯泡，则删除这个按钮
     * @param distribution{String} ：配电箱
     * @param room:房间
     * @param region:房间区域
     * @param loop:电路
     * @param panel:面板
     * @param buttons{Array}：打开灯的按钮组
     * @returns {{}}
     */
    getButtonsLamps: function (distribution, room, region, loop, panel, buttons) {
      if (!this.attributeCount(this.lightData)) {
        this.initLightData();
      }
      var currentPanel = {};
      if (this.lightData[distribution]) {
        if (this.lightData[distribution][room]) {
          if (this.lightData[distribution][room][region]) {
            if (this.lightData[distribution][room][region][loop]) {
              if (this.lightData[distribution][room][region][loop][panel]) {
                var allLamp = this.lightData[distribution][room][region][loop][panel];
                for (var i = 0; i < buttons.length; i++) {
                  currentPanel[buttons[i]] = {};
                  var count = 0;
                  for (var key in allLamp[buttons[i]]) {
                    if (allLamp[buttons[i]].hasOwnProperty(key)) {
                      currentPanel[buttons[i]][key] = allLamp[buttons[i]][key];
                      count++;
                    }
                  }
                  if (!count) {
                    delete currentPanel[buttons[i]];
                  }
                }
              }
            }
          }
        }
      }
      buttons = null;
      allLamp = null;
      return currentPanel;
    },
    /**
     * 添加打开灯光的结构
     * @param distribution
     * @param room
     * @param region
     * @param loop
     * @param panel
     * @param buttons
     */
    _addOpenedLightDir: function (distribution, room, region, loop, panel) {
      this.openedLight[distribution] = this.openedLight[distribution] || {};
      this.openedLight[distribution][room] = this.openedLight[distribution][room] || {};
      this.openedLight[distribution][room][region] = this.openedLight[distribution][room][region] || {};
      this.openedLight[distribution][room][region][loop] = this.openedLight[distribution][room][region][loop] || {};
      this.openedLight[distribution][room][region][loop][panel] = this.openedLight[distribution][room][region][loop][panel] || {};
    },

    /**
     * 添加实际光源
     * @param distribution
     * @param room
     * @param room
     * @param level
     * @param dataDictionary
     * @private
     */
    _addRoomSourceLights: function (distribution, room, level) {
      for (var region in this.openedLight[distribution][room]) {
        if (this.openedLight[distribution][room].hasOwnProperty(region)) {

          for (var loop in this.openedLight[distribution][room][region]) {
            if (this.openedLight[distribution][room][region].hasOwnProperty(loop)) {

              for (var panel in this.openedLight[distribution][room][region][loop]) {
                if (this.openedLight[distribution][room][region][loop].hasOwnProperty(panel)) {

                  for (var button in this.openedLight[distribution][room][region][loop][panel]) {
                    if (this.openedLight[distribution][room][region][loop][panel].hasOwnProperty(button)) {

                      for (var item in this.openedLight[distribution][room][region][loop][panel][button]) {
                        if (this.openedLight[distribution][room][region][loop][panel][button].hasOwnProperty(item)) {

                          // 添加光源
                          var lamp = this.openedLight[distribution][room][region][loop][panel][button][item];
                          if (lamp.level && lamp.level.indexOf(level) > -1) {
                            // 记录当前打开的灯光
                            if (!lamp.spotLight) {
                              var dataDictionary = new THREE.DataDictionary();
                              var vertexes = dataDictionary.getMaterialVertexes(lamp.mesh, lamp.materialIndex);
                              var vector3Array = new THREE.Vector3Array(vertexes);
                              var center = vector3Array.getCenter();
                              dataDictionary.getWorldPosition(center, lamp.mesh);
                              this.addSpotLight(center, lamp, {});
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },

    // eachBulbLights: function (callback) {
    //   for (var i = 0; i < this.bulbLights.length; i++) {
    //     callback(this.bulbLights[i]);
    //   }
    // },
    /**
     * 添加聚光灯并添加管理
     * @param pos{THREE.Vector3} 位置
     * @param data_position 保存数据的位置
     * @param option {Object}
     *  addHelper {Boolean} true添加灯光的辅助线 false 不添加 默认 false
     */
    addSpotLight: function (pos, data_position, option) {
      option = option || {};
      var lightPos = pos !== undefined ? pos : this.pos;

      var spotLight = new THREE.SpotLight(this.spotLightColor, this.spotLightIntensity);
      spotLight.castShadow = true;
      spotLight.distance = this.spotLightDistance;
      spotLight.angle = this.spotLightAngle;
      spotLight.penumbra = this.spotLightPenumbra;
      spotLight.decay = this.spotLightDecay;
      spotLight.shadow.mapSize.width = 1024;
      spotLight.shadow.mapSize.height = 1024;
      spotLight.position.copy(lightPos);

      data_position.spotLight = spotLight;
      this.scene.add(spotLight);

      var targetObject = new THREE.Object3D();
      targetObject.position.copy(lightPos);
      targetObject.position.y -= 10;
      this.scene.add(targetObject);
      spotLight.target = targetObject;
      data_position.target = targetObject;

      if (option.addHelper) {
        this.lightHelper = new THREE.SpotLightHelper(spotLight);
        this.scene.add(this.lightHelper);
      }

      if (option.addBulb) {
        var bulbGeometry = new THREE.SphereGeometry(0.2, 16, 8);
        var bulbMat = new THREE.MeshStandardMaterial({
          emissive: this.spotLightColor,
          emissiveIntensity: 1,
          color: 0x000000
        });
        var lightMesh = new THREE.Mesh(bulbGeometry, bulbMat);
        spotLight.add(lightMesh);
      }

      data_position = null;
      spotLight = null;
      targetObject = null;
    },
    /*eachSportLights: function (callback) {
      for (var i = 0; i < this.spotLights.length; i++) {
        callback(this.spotLights[i]);
      }
    },*/

    /**
     * 打开指定的灯
     * @param distribution{String} ：配电箱
     * @param room:房间
     * @param region:房间区域
     * @param loop:电路
     * @param panel:面板
     * @param buttons{Array}：打开灯的按钮组
     */
    openLight: function (distribution, room, region, loop, panel, buttons) {

      // 检查数据
      if (buttons === undefined || buttons.length === 0) {
        return;
      }

      // 查找当前打开的灯
      var buttons_lamps = this.getButtonsLamps(distribution, room, region, loop, panel, buttons);
      if (!this.attributeCount(buttons_lamps)) return; // 如果没有找到一个灯泡，则不进行任何操作

      // 初始化当前要打开灯以及灯的路径
      this._addOpenedLightDir(distribution, room, region, loop, panel);
      Object.assign(this.openedLight[distribution][room][region][loop][panel], buttons_lamps);

      // 获取设置光源的级别
      var level = this._findLevelByCount(distribution, room);

      // 关闭多余的光源
      if (!(distribution === this.openedLight.currentDistribution && room === this.openedLight.currentRoom)) {
        this.deleteRoomSourceLight(this.openedLight.currentDistribution, this.openedLight.currentRoom, null, false);
      }
      if (distribution === this.openedLight.currentDistribution && room === this.openedLight.currentRoom && level !== this.openedLight.currentLevel) {
        this.deleteRoomSourceLight(this.openedLight.currentDistribution, this.openedLight.currentRoom, level, false);
      }

      var materialController = new LR.MaterialController();
      // 打开灯
      for (var button in buttons_lamps) {
        if (buttons_lamps.hasOwnProperty(button)) {

          var lamps = buttons_lamps[button];
          for (var key in lamps) {
            if (lamps.hasOwnProperty(key)) {

              // 查找灯泡材质id
              var materialIndex = lamps[key].materialIndex;
              var mesh = lamps[key].mesh;

              // 修改灯光材质
              materialController.lightChangeColor(mesh, materialIndex);
            }
          }
        }
      }

      // 获取所有打开的灯，添加灯光效果
      this._addRoomSourceLights(distribution, room, level);

      // 记录当前添加实际光源的配电箱、房间、添加级别
      this.openedLight.currentDistribution = distribution;
      this.openedLight.currentRoom = room;
      this.openedLight.currentLevel = level;

      mesh = null;
      lamps = null;
    },
    /**
     * 关闭指定线路的灯
     * @param distribution{String} ：配电箱
     * @param room:房间
     * @param region:房间区域
     * @param loop:电路
     * @param panel:面板
     * @param buttons{Array}：打开灯的按钮组
     */
    closeLight: function (distribution, room, region, loop, panel, buttons) {
      var _panel = {};
      // 数据检测
      try {
        _panel = this.openedLight[distribution][room][region][loop][panel];
      } catch (e) {
        return;
      }
      if (buttons === undefined || buttons.length === 0) {
        return;
      }

      for (var button_key in buttons) {
        if (buttons.hasOwnProperty(button_key)) {

          // 删除路径上的灯光
          this.deleteButtonSourceLight(_panel[buttons[button_key]], null, true);

          // 删除保存的光照信息
          delete _panel[buttons[button_key]];
        }
      }

      // 删除没有用到的灯光的结构
      this._deleteOpenedLightDir(distribution, room, region, loop, panel);

      // 删除当前保存的实际光源的配电箱、房间、添加级别
      if (!(this.openedLight[this.openedLight.currentDistribution] && this.openedLight[this.openedLight.currentDistribution][this.openedLight.currentRoom])) {
        delete this.openedLight.currentDistribution;
        delete this.openedLight.currentRoom;
        delete this.openedLight.currentLevel;
      }
    },
    /**
     * 删除所有灯光
     */
    closeAllLight: function () {
      // 删除灯光
      for (var distribution in this.openedLight) {
        if (this.openedLight.hasOwnProperty(distribution)) {

          for (var room in this.openedLight[distribution]) {
            if (this.openedLight[distribution].hasOwnProperty(room)) {

              this.deleteRoomSourceLight(distribution, room, null, true);
            }
          }
        }
      }
    },
    /**
     * 删除灯光的结构
     * @param distribution
     * @param room
     * @param region
     * @param loop
     * @param panel
     * @private
     */
    _deleteOpenedLightDir: function (distribution, room, region, loop, panel) {
      if (this.attributeCount(this.openedLight[distribution][room][region][loop][panel]) <= 0) {
        delete this.openedLight[distribution][room][region][loop][panel];
        if (this.attributeCount(this.openedLight[distribution][room][region][loop]) <= 0) {
          delete this.openedLight[distribution][room][region][loop];
          if (this.attributeCount(this.openedLight[distribution][room][region]) <= 0) {
            delete this.openedLight[distribution][room][region];
            if (this.attributeCount(this.openedLight[distribution][room]) <= 0) {
              delete this.openedLight[distribution][room];
              if (this.attributeCount(this.openedLight[distribution]) <= 0) {
                delete this.openedLight[distribution];
              }
            }
          }
        }
      }
    },
    /**
     * 去掉房间除了给定级别的光源
     * @param distribution 配电柜
     * @param room 房间
     * @param level 设置光源的级别
     * @param isChangMaterial 是否恢复灯的材质
     */
    deleteRoomSourceLight: function (distribution, room, level, isChangMaterial) {
      if (distribution === undefined || room === undefined) {
        return;
      }

      for (var region in this.openedLight[distribution][room]) {
        if (this.openedLight[distribution][room].hasOwnProperty(region)) {

          for (var loop in this.openedLight[distribution][room][region]) {
            if (this.openedLight[distribution][room][region].hasOwnProperty(loop)) {

              for (var panel in this.openedLight[distribution][room][region][loop]) {
                if (this.openedLight[distribution][room][region][loop].hasOwnProperty(panel)) {

                  for (var button in this.openedLight[distribution][room][region][loop][panel]) {
                    if (this.openedLight[distribution][room][region][loop][panel].hasOwnProperty(button)) {
                      this.deleteButtonSourceLight(this.openedLight[distribution][room][region][loop][panel][button], level, isChangMaterial);
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    /**
     * 去掉除了给定级别level的灯泡上的光源
     * @param bulbs
     * @param level：不关闭的级别
     * @param isChangMaterial:是否把灯管材质切换回去
     */
    deleteButtonSourceLight: function (bulbs, level, isChangMaterial) {
      var materialControl = new LR.MaterialController();

      // 从场景中删除灯光，并把灯光从保存的位置删除
      for (var item in bulbs) {
        if (bulbs.hasOwnProperty(item)) {

          // 删除材质
          if (isChangMaterial) {
            var mesh = bulbs[item].mesh;
            materialControl.closeLightChange(mesh);
          }

          if (bulbs[item].level && (bulbs[item].level.indexOf(level) > -1)) {
            continue;
          }
          if (bulbs[item].spotLight) {
            this.scene.remove(bulbs[item].spotLight);
            this.scene.remove(bulbs[item].target);
            delete bulbs[item].spotLight;
            delete bulbs[item].target;
          }
        }
      }

      bulbs = null;
    },
    /**
     * 检测对象中有几个属性
     * @param object 检测对象
     * @return {number} 对象中属性的个数，如果传入的不是对象，返回-1
     */
    attributeCount: function (object) {
      if ((typeof object) !== "object") {
        return -1;
      }

      var count = 0;
      for (var key in object) {
        if (object.hasOwnProperty(key)) {
          count++
        }
      }
      return count;
    },
    /**
     * 根据打开的所有的灯光，计算出添加光源的级别
     * @param{String} distribution 配电箱
     * @param{String} room 房间
     * @return{String} 添加光源的级别
     * @private
     */
    _findLevel: function (distribution, room) {
      if (this.attributeCount(this.openedLight[distribution][room]) > 1) {
        return "A"
      }

      for (var region in this.openedLight[distribution][room]) {
        if (this.openedLight[distribution][room].hasOwnProperty(region)) {
          if (this.attributeCount(this.openedLight[distribution][room][region]) > 1) {
            return "B"
          }

          for (var loop in this.openedLight[distribution][room][region]) {
            if (this.openedLight[distribution][room][region].hasOwnProperty(loop)) {
              if (this.attributeCount(this.openedLight[distribution][room][region][loop]) > 1) {
                return "C";
              }

              for (var panel in this.openedLight[distribution][room][region][loop]) {
                if (this.openedLight[distribution][room][region][loop].hasOwnProperty(panel)) {
                  if (this.attributeCount(this.openedLight[distribution][room][region][loop][panel]) > 1) {
                    return "E";
                  } else if (this.attributeCount(this.openedLight[distribution][room][region][loop][panel]) === 1) {
                    return "D";
                  }
                }
              }
            }
          }
        }
      }
    },
    /**
     * 根据打开的所有的灯光，计算出添加光源的级别
     * @param{String} distribution 配电箱
     * @param{String} room 房间
     * @return{String} 添加光源的级别
     * @private
     */
    _findLevelByCount: function (distribution, room) {
      var count_A = 0;
      var count_B = 0;
      var count_C = 0;
      var count_D = 0;
      var count_E = 0;
      for (var region in this.openedLight[distribution][room]) {
        if (this.openedLight[distribution][room].hasOwnProperty(region)) {

          for (var loop in this.openedLight[distribution][room][region]) {
            if (this.openedLight[distribution][room][region].hasOwnProperty(loop)) {

              for (var panel in this.openedLight[distribution][room][region][loop]) {
                if (this.openedLight[distribution][room][region][loop].hasOwnProperty(panel)) {

                  for (var button in this.openedLight[distribution][room][region][loop][panel]) {
                    if (this.openedLight[distribution][room][region][loop][panel].hasOwnProperty(button)) {

                      for (var item in this.openedLight[distribution][room][region][loop][panel][button]) {
                        if (this.openedLight[distribution][room][region][loop][panel][button].hasOwnProperty(item)) {
                          if (this.openedLight[distribution][room][region][loop][panel][button][item].level) {
                            if (this.openedLight[distribution][room][region][loop][panel][button][item].level.indexOf("A") > -1) {
                              count_A += 1;
                            }
                            if (this.openedLight[distribution][room][region][loop][panel][button][item].level.indexOf("B") > -1) {
                              count_B += 1;
                            }
                            if (this.openedLight[distribution][room][region][loop][panel][button][item].level.indexOf("C") > -1) {
                              count_C += 1;
                            }
                            if (this.openedLight[distribution][room][region][loop][panel][button][item].level.indexOf("D") > -1) {
                              count_D += 1;
                            }
                            if (this.openedLight[distribution][room][region][loop][panel][button][item].level.indexOf("E") > -1) {
                              count_E += 1;
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }

      if (count_D <= 10 && count_D > 0) {
        return "D";
      }
      else if (count_E > 0 && count_E <= 10) {
        return "E";
      }
      else if (count_C > 0 && count_C <= 10) {
        return "C";
      }
      else if (count_B > 0 && count_B <= 10) {
        return "B"
      }
      else {
        return "A";
      }
    }
  });

  /**
   * html结构
   * <span id="musicControl">
   *   <a id="mc_play" class="stop" onclick="play_music();">
   *     <audio id="musicfx" loop="loop">
   *       <source src="../music/mp3/AL Rocco、小安迪 - China Wave (Live).mp3" type="audio/mpeg">
   *     </audio>
   *   </a>
   * </span>
   * @param url 播放的路径
   * @param options
   *  container：挂载点 《body》
   *  autoplay：是否自动播放 false
   *  loop: 控制是否循环播放 true
   * @constructor
   */
  LR.MusicControl = function (url, options) {
    options = options || {};

    this.url = url;
    this.container = options.container !== undefined ? options.container : document.getElementsByTagName("body")[0];
    this.autoplay = options.autoplay !== undefined ? options.autoplay : false;
    this.loop = options.loop !== undefined ? options.loop : true;

    this.aNode = document.createElement("a");
    this.audioNode = document.createElement("audio");

    this._createDom();
  };
  Object.assign(LR.MusicControl.prototype, {
    // 创建dom结构
    _createDom: function () {
      var controlNode = document.createElement("div");
      controlNode.id = "musicControl";

      this._playMusic = this._bind(this, this.playMusic);
      this.aNode.addEventListener("click", this._playMusic);

      this.audioNode.loop = this.loop;
      if (this.autoplay) {
        this.justPlay();
      } else {
        this.justPause();
      }

      var sourceNode = document.createElement("source");
      sourceNode.src = this.url;
      sourceNode.type = "audio/mpeg";

      this.audioNode.appendChild(sourceNode);
      this.aNode.appendChild(this.audioNode);
      controlNode.appendChild(this.aNode);
      this.container.appendChild(controlNode);
    },
    /**
     * 播放音乐
     */
    justPlay: function () {
      try {
        this.audioNode.play();
      } catch (e) {
        this.audioNode.autoplay = true;
      }
      this.aNode.setAttribute("class", "on");
    },
    /**
     * 播放音乐
     */
    justPause: function () {
      this.audioNode.pause();
      this.aNode.setAttribute("class", "stop");
    },
    /**
     * 控制播放音乐
     */
    playMusic: function () {
      if (this.aNode.getAttribute("class") === "on") {
        this.justPause();
      } else {
        this.justPlay();
      }
    },

    /**
     * 控制音乐的播放
     * @param isPlay true 播放 false 停止
     */
    controlMusicPlay: function (isPlay) {
      if (isPlay) {
        this.justPlay();
      } else {
        this.justPause();
      }
    },

    _bind: function (scope, fn) {
      return function () {
        fn.apply(scope, arguments);
      };
    }
  });

  /**
   * 管理场景中的故障
   * @param baseFileUrl 图片路径
   * @constructor
   */
  LR.FaultDetectionControl = function (baseFileUrl) {
    this.baseFileUrl = baseFileUrl !== undefined ? baseFileUrl : "img/alarm/";
    this.faultObjects = {}; // 保存错位的对象
  };
  Object.assign(LR.FaultDetectionControl.prototype, {
    /**
     * 添加故障检测
     * @param objectID 故障对象id
     * @param scene 场景对象
     * @param options
     *  filename: 图片名称
     *  faultObject: 报警设备
     * @returns {null}
     */
    addFaultIdentification: function (objectID, scene, options) {
      if (this.faultObjects[objectID]) {
        return null;
      }
      var dataDictionary = new THREE.DataDictionary();

      options = options || {};
      var filename = options.filename !== undefined ? options.filename : "alarm.png";
      var faultObject = options.faultObject !== undefined ? options.faultObject : dataDictionary.findObjectByID(scene, objectID);
      if (!faultObject) {
        return null;
      }

      var vector3Array = dataDictionary.computeBoundingBox(faultObject);

      var spriteMap = new THREE.TextureLoader().load(this.baseFileUrl + filename);
      var spriteMaterial = new THREE.SpriteMaterial({map: spriteMap, color: 0xffffff});
      var sprite = new THREE.Sprite(spriteMaterial);
      sprite.position.set((vector3Array.min.x + vector3Array.max.x) / 2, vector3Array.min.y - 0.3, (vector3Array.min.z + vector3Array.max.z) / 2);
      sprite.scale.set(1, 1, 1);
      scene.add(sprite);
      setInterval(function () {
        sprite.visible = !sprite.visible;
      }, 1000);

      this.faultObjects[objectID] = {};
      this.faultObjects[objectID].sprite = sprite;
    }
  });

  /**
   * 管理设备警报
   * @constructor
   */
  LR.Alarms = function () {
    this.meshes = [];
  };
  Object.assign(LR.Alarms.prototype, {
    add: function (mesh) {
      // 检查防止添加多次
      for (var i = 0; i < this.meshes.length; i++) {
        if (this.meshes[i] === mesh) {
          return;
        }
      }

      this.meshes.push(mesh);
    },
    update: function () {
      for (var i = 0; i < this.meshes.length; i++) {
        if (this.meshes[i].userData.TYPE === LR.MaterialController.ALARM_TYPE) {
          if (this.meshes[i].userData.alarmChanged) {
            this.meshes[i].material = this.meshes[i].userData.AlarmMaterial;
            this.meshes[i].userData.alarmChanged = false;
          } else {
            this.meshes[i].userData.AlarmMaterial = this.meshes[i].material;
            this.meshes[i].material = this.meshes[i].userData.sourceMaterial;
            this.meshes[i].userData.alarmChanged = true;
          }
        }
      }
    },
    animate: function () {
      var _this = this;
      setInterval(function () {
        _this.update();
      }, 1000)
    }
  });

  /**
   * 风机
   * @constructor
   */
  LR.Fan = function (mesh) {
    this.mesh = mesh;

    this.rotateZSpeedUnit = -Math.PI / 360; // 转动的单位
    this.rotateZSpeed = 0; // 转动的速度
    this.rotateZSpeedMax = 2000; // 转动的最大值
    this.rotateZSpeedMin = 0; // 转动的最小值
    this.rotateZInertia = 200; // 转动的惯性

    this.enable = true; // 标记是转动还是停止 true转动 false 停止

    this.RotatingObject = new THREE.RotatingObject(mesh);
    this.RotatingObject.setOriginCenter();
  };
  Object.assign(LR.Fan.prototype, {
    update: function (delta) {
      if (this.enable === false && this.rotateZSpeed <= this.rotateZSpeedMin) {
        return;
      }

      if (this.enable === false && this.rotateZSpeed >= this.rotateZSpeedMin) {
        this.rotateZSpeed -= this.rotateZInertia * delta;
      }

      if (this.enable === true && this.rotateZSpeed <= this.rotateZSpeedMax) {
        this.rotateZSpeed += this.rotateZInertia * delta;
      }

      delta = delta || 1;
      var speed = this.rotateZSpeedUnit * delta * this.rotateZSpeed;

      this.RotatingObject.rotateX(speed);
    }
  });
  /**
   * 管理旋转的风机
   * @constructor
   */
  LR.Fans = function () {
    this.rotatingFans = [];
  };
  Object.assign(LR.Fans.prototype, {
    /**
     * 添加风机,并启动旋转
     * @param mesh
     */
    addFan: function (mesh) {
      var fan = null;
      // 检查当前风机是否存在
      for (var i = 0; i < this.rotatingFans.length; i++) {
        if (this.rotatingFans[i].mesh === mesh) {
          fan = this.rotatingFans[i];
        }
      }
      if (!fan) {
        fan = new LR.Fan(mesh);
        this.rotatingFans.push(fan);
      }

      fan.enable = true;
    },

    /**
     * @param mesh 停止方法
     */
    stopFan: function (mesh) {
      // 检查当前风机是否打开,如果打开则删除这个风机
      for (var i = 0; i < this.rotatingFans.length; i++) {
        if (this.rotatingFans[i].mesh === mesh) {
          this.rotatingFans[i].enable = false;
        }
      }
    },

    /**
     * 删除所有的风机
     */
    deleteFans: function () {
      this.rotatingFans = [];
    },

    /**
     * 更新动画
     * @param delta
     */
    update: function (delta) {
      for (var i = 0; i < this.rotatingFans.length; i++) {
        this.rotatingFans[i].update(delta);
      }
    }
  });

  /**
   * 水箱水位变化
   * @param mesh
   * @param object3D
   * @param baseURL{String}
   * @constructor
   */
  LR.Tank = function (mesh, object3D, baseURL) {
    this.mesh = mesh;
    this.object3D = object3D;
    this.baseUrl = baseURL !== undefined ? baseURL : "";

    // 水箱
    this.tank_texture_url = this.baseUrl + "img/tank/tank.jpg";
    this.width = 0;
    this.height = 0;
    this.depth = 0;
    this.position = new THREE.Vector3();

    // 水位
    this.water_level = null;
    this.water_level_height = 0.4; // 水位的高度比例

    // 水波纹
    this.water_texture_url = this.baseUrl + "img/tank/waternormals.jpg";
    this.waterWave = null;

    this.init();
  };
  Object.assign(LR.Tank.prototype, {
    init: function () {
      if (!this.mesh.geometry.boundingBox) {
        this.mesh.geometry.computeBoundingBox();
      }
      var max = this.mesh.geometry.boundingBox.max;
      var min = this.mesh.geometry.boundingBox.min;
      this.width = max.x - min.x;
      this.height = max.y - min.y;
      this.depth = max.z - min.z;

      this.position.copy(this.mesh.position);
      this.position.x += (max.x + min.x) / 2;
      this.position.y += (max.y + min.y) / 2;
      this.position.z += (max.z + min.z) / 2;

      var texture = new THREE.TextureLoader().load(this.tank_texture_url);
      texture.wrapS = THREE.RepeatWrapping;
      texture.wrapT = THREE.RepeatWrapping;
      texture.repeat.set(0.1, 0.1);
      this.mesh.material[0].color = new THREE.Color(0xffffff);
      this.mesh.material[0].map = texture;
      this.mesh.material[0].side = THREE.DoubleSide;
      this.mesh.material[0].shadowSide = THREE.BackSide;
      this.mesh.material[0].needsUpdate = true;

      // 水位
      var water_level_geometry = new THREE.BoxGeometry(this.width, this.height, this.depth);
      var water_level_material = new THREE.MeshBasicMaterial({
        color: 0x54ffff,
        transparent: true,
        opacity: 0.4,
        polygonOffset: true,
        polygonOffsetFactor: 1,
        polygonOffsetUnits: 1
      });
      this.water_level = new THREE.Mesh(water_level_geometry, water_level_material);
      this.water_level.applyMatrix(this.mesh.matrix.clone());
      this.water_level.castShadow = true;
      this.water_level.receiveShadow = true;
      this._calculateWaterLevel();
      this.water_level.userData.instance = this.mesh.userData.instance;
      this.water_level.visible = false;
      this.object3D.add(this.water_level);

      // 水波
      var waterGeometry = new THREE.PlaneBufferGeometry(this.width, this.height);
      this.waterWave = new THREE.Water(
        waterGeometry,
        {
          textureWidth: 64,
          textureHeight: 64,
          waterNormals: new THREE.TextureLoader().load(this.water_texture_url, function (texture) {
            texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
          }),
          alpha: 0.5,
          sunColor: 0xffffff,
          waterColor: 0x48828e
        }
      );
      this.waterWave.applyMatrix(this.mesh.matrix.clone());
      this._calculateWaterWave();
      this.waterWave.visible = false;
      this.object3D.add(this.waterWave);
    },
    update: function () {
      if (this.waterWave) {
        this.waterWave.material.uniforms.time.value += 1.0 / 30.0;
      }
    },
    updateWaterLevel: function (water_level_height) {
      this.water_level_height = parseFloat(water_level_height);

      // 初始化水波,水
      this.mesh.material[0].side = THREE.BackSide;
      this.water_level.visible = true;
      this.waterWave.visible = true;

      this._calculateWaterLevel();
      this._calculateWaterWave();
    },
    _calculateWaterLevel: function () {
      this.water_level.position.set(this.position.x, this.position.y, this.position.z - this.depth / 2 * (1 - this.water_level_height));
      this.water_level.scale.z = this.water_level_height;
    },
    _calculateWaterWave: function () {
      this.waterWave.position.set(this.position.x, this.position.y, this.position.z + this.depth * this.water_level_height - this.depth / 2);
    }
  });

  LR.Tanks = function (baseURL) {
    this.baseUrl = baseURL;
    this.tanks = [];
  };
  Object.assign(LR.Tanks.prototype, {
    /**
     * @param mesh
     * @param object3D
     */
    addTank: function (mesh, object3D) {
      var tank = new LR.Tank(mesh, object3D, this.baseUrl);
      this.tanks.push(tank);
    },
    update: function () {
      for (var i = 0; i < this.tanks.length; i++) {
        this.tanks[i].update();
      }
    },
    removeAll: function () {
      for (var i = 0; i < this.tanks.length; i++) {
        var tank = this.tanks[i];
        tank.water_level = null;
        tank.waterWave = null;
      }
      this.tanks = [];
    },

    /**
     * 更新水箱的水位变化
     * @param scale
     * @param id
     */
    updateWaterLevel: function (id, scale) {
      for (var i = 0; i < this.tanks.length; i++) {
        if (this.tanks[i].mesh.userData.instance.id === id.toString()) {
          this.tanks[i].updateWaterLevel(scale);
        }
      }
    },

    /**
     * 删除所有的水波,水
     */
    deleteAllWater: function () {
      for (var i = 0; i < this.tanks.length; i++) {
        this.tanks[i].mesh.material[0].side = THREE.DoubleSide;
        this.tanks[i].mesh.material.needsUpdate = true;
        this.tanks[i].water_level.visible = false;
        this.tanks[i].waterWave.visible = false;
      }
    }
  });
})(LR);

// 加载数据
(function (LR) {
  LR.findMesh = (function () {
    return function (data, attributes, objectPosition, callback) {
      attributes = attributes || {};

      for (var key in data) {
        if (data.hasOwnProperty(key)) {

          var json_instance = data[key];

          var _attributes = {};
          _attributes.id = json_instance.id || attributes.id;
          _attributes.name = json_instance.name || attributes.name;
          _attributes.BuiltInCategory = json_instance.BuiltInCategory || attributes.BuiltInCategory;
          _attributes.FamilyAndType = json_instance.FamilyAndType || attributes.FamilyAndType;
          _attributes.Width = json_instance.Width || attributes.Width;
          _attributes.Height = json_instance.Height || attributes.Height;
          _attributes.Volume = json_instance.Volume || attributes.Volume;
          _attributes.Area = json_instance.Area || attributes.Area;
          if (json_instance.Annotation) {
            try {
              _attributes.Annotation = JSON.parse(json_instance.Annotation);
            } catch (e) {
              _attributes.Annotation = json_instance.Annotation;
            }
          } else {
            _attributes.Annotation = attributes.Annotation;
          }

          if (json_instance.hasOwnProperty("childInstances")) {
            var object = new THREE.Object3D();
            object.userData.instance = _attributes;

            objectPosition.add(object);
            LR.findMesh(json_instance.childInstances, _attributes, object, callback);
          }
          else {
            if (json_instance.lods.length > 0) {
              callback(json_instance, _attributes, objectPosition);
            }
          }
        }
      }
    }
  })();
  LR.getAnnotation = (function () {
    var Annotation = {};
    return function (objectPosition) {
      if (objectPosition.userData.instance && objectPosition.userData.instance.Annotation) {
        Annotation = objectPosition.userData.instance.Annotation
      } else {
        if (objectPosition.parent.constructor === THREE.Object3D) {
          Annotation = LR.getAnnotation(objectPosition.parent);
        }
      }
      return Annotation;
    }
  })();
})(LR);

Array.prototype.isArrayFn = (function () {
  return function (value) {
    if (typeof Array.isArray === "function") {
      return Array.isArray(value);
    } else {
      return Object.prototype.toString.call(value) === "[object Array]";
    }
  };
})();